// File: jmcomic-core/src/main/java/dev/jukomu/common/postman/impl/OkHttpPostman.java
package dev.jukomu.common.postman.impl;

import dev.jukomu.common.postman.AbstractPostman;
import dev.jukomu.common.postman.Postman;
import dev.jukomu.common.util.json.JsonUtils;
import dev.jukomu.common.util.net.CommonResponse;
import dev.jukomu.common.util.net.IResponse;
import okhttp3.*;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 对应Python的RequestsPostman，基于OkHttp实现的Postman。
 * 每个请求都是独立的，不共享Session。
 */
public class OkHttpPostman extends AbstractPostman {

    public static String POSTMAN_KEY = "okhttp"; // 对应Python的postman_key

    private final OkHttpClient defaultClient; // 用于执行请求的OkHttpClient实例

    /**
     * 构造函数。
     *
     * @param kwargs 初始化参数Map，包含HTTP客户端配置（如headers, proxies, timeout等）。
     */
    public OkHttpPostman(Map<String, Object> kwargs) {
        super(kwargs);
        this.defaultClient = buildClient(kwargs);
    }

    // 辅助方法：构建OkHttpClient实例
    protected OkHttpClient buildClient(Map<String, Object> kwargs) {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();

        // 处理timeout
        Number timeout = (Number) kwargs.get("timeout");
        if (timeout != null) {
            clientBuilder.connectTimeout(timeout.longValue(), TimeUnit.SECONDS)
                    .readTimeout(timeout.longValue(), TimeUnit.SECONDS)
                    .writeTimeout(timeout.longValue(), TimeUnit.SECONDS);
        } else {
            // Default timeouts if not specified, OkHttp has its own defaults (10s)
        }

        // 处理allow_redirects
        Boolean allowRedirects = (Boolean) kwargs.get("allow_redirects");
        if (allowRedirects != null) {
            clientBuilder.followRedirects(allowRedirects);
            clientBuilder.followSslRedirects(allowRedirects);
        }

        // 处理proxies
        Map<String, String> proxies = (Map<String, String>) kwargs.get("proxies");
        if (proxies != null && !proxies.isEmpty()) {
            // OkHttp 的代理配置通常是单一的，需要根据Map中的协议来决定使用哪种代理
            // Python的proxies是{"http": "...", "https": "..."}，OkHttp的proxy()接受一个java.net.Proxy
            // 如果同时提供了http和socks代理，需要选择一个。这里优先socks，其次http/https
            String socksAddress = proxies.get("socks");
            if (socksAddress != null && socksAddress.startsWith("socks://")) {
                String[] parts = socksAddress.substring("socks://".length()).split(":");
                if (parts.length == 2) {
                    clientBuilder.proxy(new java.net.Proxy(java.net.Proxy.Type.SOCKS, new java.net.InetSocketAddress(parts[0], Integer.parseInt(parts[1]))));
                }
            } else {
                String httpAddress = proxies.get("http"); // Can be http:// or https://
                if (httpAddress != null && (httpAddress.startsWith("http://") || httpAddress.startsWith("https://"))) {
                    String cleanAddress = httpAddress.replaceFirst("^(http|https)://", "");
                    String[] parts = cleanAddress.split(":");
                    if (parts.length == 2) {
                        clientBuilder.proxy(new java.net.Proxy(java.net.Proxy.Type.HTTP, new java.net.InetSocketAddress(parts[0], Integer.parseInt(parts[1]))));
                    }
                }
            }
        }

        // 处理verify (SSL验证)
        Boolean verify = (Boolean) kwargs.get("verify");
        if (verify != null && !verify) {
            // 信任所有证书，不推荐用于生产环境
            clientBuilder.hostnameVerifier((hostname, session) -> true);
            try {
                javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[]{
                        new javax.net.ssl.X509TrustManager() {
                            @Override
                            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
                            }

                            @Override
                            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
                            }

                            @Override
                            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                                return new java.security.cert.X509Certificate[]{};
                            }
                        }
                };
                javax.net.ssl.SSLContext sslContext = javax.net.ssl.SSLContext.getInstance("SSL");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
                clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), (javax.net.ssl.X509TrustManager) trustAllCerts[0]);
            } catch (Exception e) {
                System.err.println("Failed to disable SSL verification: " + e.getMessage());
            }
        }

        // 处理cookies (通过CookieJar)
        // OkHttp的CookieJar是会话级的，对于OkHttpPostman（非Session），通常不会设置CookieJar
        // Cookies通常通过header手动添加，或者在Postman代理链中处理
        Map<String, Object> cookies = (Map<String, Object>) kwargs.get("cookies");
        if (cookies != null && !cookies.isEmpty()) {
            // 对于非sessionClient，cookies需要手动添加到每个请求的Header中
            // 这将在getRequestExecutor()和postRequestExecutor()内部处理
        }

        // 处理impersonate：对于OkHttp，impersonate参数会被忽略
        // Python的impersonate通常用于curl_cffi模拟浏览器指纹
        // 这里只是打印警告
        if (kwargs.containsKey("impersonate")) {
            System.err.println("Warning: 'impersonate' parameter is ignored by OkHttpPostman as it's specific to curl_cffi.");
        }

        return clientBuilder.build();
    }

    @Override
    protected Postman getRequestExecutor() {
        return new OkHttpExecutor(defaultClient, metaData);
    }

    @Override
    protected Postman postRequestExecutor() {
        return new OkHttpExecutor(defaultClient, metaData);
    }

    @Override
    public Postman copy() {
        // 创建一个包含当前metaData副本的新实例
        return new OkHttpPostman(new HashMap<>(metaData));
    }

    // 内部类，执行OkHttp请求
    protected static class OkHttpExecutor implements Postman {
        private final OkHttpClient client;
        private final Map<String, Object> defaultMetaData; // 默认元数据，如headers, cookies等

        public OkHttpExecutor(OkHttpClient client, Map<String, Object> defaultMetaData) {
            this.client = client;
            this.defaultMetaData = defaultMetaData;
        }

        private Headers buildHeaders(Map<String, Object> requestKwargs) {
            Headers.Builder headersBuilder = new Headers.Builder();

            // Apply default headers from metaData
            Map<String, Object> defaultHeaders = (Map<String, Object>) defaultMetaData.get("headers");
            if (defaultHeaders != null) {
                defaultHeaders.forEach((k, v) -> headersBuilder.add(k, String.valueOf(v)));
            }

            // Apply cookies from metaData
            Map<String, Object> defaultCookies = (Map<String, Object>) defaultMetaData.get("cookies");
            if (defaultCookies != null && !defaultCookies.isEmpty()) {
                String cookieHeader = defaultCookies.entrySet().stream()
                        .map(e -> e.getKey() + "=" + e.getValue())
                        .collect(Collectors.joining("; "));
                if (!cookieHeader.isEmpty()) {
                    headersBuilder.add("Cookie", cookieHeader);
                }
            }

            // Apply request-specific headers
            Map<String, Object> requestHeaders = (Map<String, Object>) requestKwargs.get("headers");
            if (requestHeaders != null) {
                requestHeaders.forEach((k, v) -> headersBuilder.add(k, String.valueOf(v)));
            }

            // Apply request-specific cookies (will override default if same key)
            Map<String, Object> requestCookies = (Map<String, Object>) requestKwargs.get("cookies");
            if (requestCookies != null && !requestCookies.isEmpty()) {
                String cookieHeader = requestCookies.entrySet().stream()
                        .map(e -> e.getKey() + "=" + e.getValue())
                        .collect(Collectors.joining("; "));
                if (!cookieHeader.isEmpty()) {
                    headersBuilder.add("Cookie", cookieHeader);
                }
            }
            return headersBuilder.build();
        }

        private HttpUrl buildHttpUrlWithParams(String url, Map<String, Object> requestKwargs) {
            HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
            Map<String, Object> params = (Map<String, Object>) requestKwargs.get("params");
            if (params != null) {
                params.forEach((k, v) -> urlBuilder.addQueryParameter(k, String.valueOf(v)));
            }
            return urlBuilder.build();
        }

        @Override
        public IResponse get(String url, Map<String, Object> kwargs) throws Exception {
            Map<String, Object> actualKwargs = kwargs != null ? kwargs : Collections.emptyMap();
            Request request = new Request.Builder()
                    .url(buildHttpUrlWithParams(url, actualKwargs))
                    .headers(buildHeaders(actualKwargs))
                    .get()
                    .build();
            okhttp3.Response response = client.newCall(request).execute();
            return new CommonResponse(response);
        }

        @Override
        public IResponse post(String url, Map<String, Object> kwargs) throws Exception {
            Map<String, Object> actualKwargs = kwargs != null ? kwargs : Collections.emptyMap();
            RequestBody requestBody = buildRequestBody(actualKwargs);

            Request request = new Request.Builder()
                    .url(buildHttpUrlWithParams(url, actualKwargs))
                    .headers(buildHeaders(actualKwargs))
                    .post(requestBody)
                    .build();
            okhttp3.Response response = client.newCall(request).execute();
            return new CommonResponse(response);
        }

        private RequestBody buildRequestBody(Map<String, Object> requestKwargs) throws IOException, FileNotFoundException {
            Object data = requestKwargs.get("data");
            Object json = requestKwargs.get("json");
            Object files = requestKwargs.get("files"); // Not used by OkHttpPostman, handled by MultiPartPostman

            if (json != null) {
                // JSON body
                return RequestBody.create(JsonUtils.toJsonString(json), MediaType.parse("application/json; charset=utf-8"));
            } else if (data instanceof Map) {
                // Form-urlencoded data
                FormBody.Builder formBuilder = new FormBody.Builder();
                ((Map<String, Object>) data).forEach((k, v) -> formBuilder.add(k, String.valueOf(v)));
                return formBuilder.build();
            } else if (data instanceof String) {
                // Raw string data
                return RequestBody.create((String) data, MediaType.parse("text/plain; charset=utf-8"));
            } else if (data instanceof byte[]) {
                // Raw byte data
                return RequestBody.create((byte[]) data, MediaType.parse("application/octet-stream"));
            }
            // For files/multipart, MultiPartPostman should be used.
            // If files is provided here, it indicates a misconfiguration or expectation mismatch.
            // Python's requests library handles 'files' as multipart/form-data.
            // We assume MultiPartPostman handles this.

            // Default empty body if no data/json
            return RequestBody.create(new byte[0], null);
        }

        // --- Postman 接口的其余方法，在此内部执行器中不实现复杂逻辑 ---
        @Override
        public Postman copy() {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public <T> T getMetaData(String key, T defaultValue) {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Map<String, Object> getMetaData() {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Object getItem(String key) {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public void setItem(String key, Object value) {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Postman getRootPostman() {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Postman withFixUrl(String url) {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Postman withRetry(int retryTimes, Class<? extends Postman> clazz) {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Postman withMultiPart() {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Postman withWrapResp(Class<? extends IResponse> clazz) {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }

        @Override
        public Postman withRedirectCatching(Class<? extends Postman> clazz) {
            throw new UnsupportedOperationException("Not supported in internal executor.");
        }
    }
}